package com.errol.generic.file.excel.service;

import com.renjia.properties.UkerServerProperties;
import com.renjia.service.generic.file.core.pojo.FileExtension;
import com.renjia.service.generic.file.core.pojo.Filepath;
import com.renjia.service.generic.file.core.service.FileInnerService;
import com.renjia.service.generic.file.core.service.FileService;
import com.renjia.service.generic.file.excel.model.ExcelDynamicColumn;
import com.renjia.service.generic.file.excel.pojo.ExcelColumnDescriptor;
import com.renjia.service.generic.file.excel.pojo.ExcelConfig;
import com.renjia.service.generic.file.excel.pojo.ValueType;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Errol on 20/04/24.
 *
 * @author zhang xiao bin
 * @version v1.0
 * @date 2020/10/21
 * @since v4.7.6
 */
@Service
public class ExcelInnerWriterServiceImpl {

    private final ExcelInnerServiceImpl excelInnerService;
    private final FileInnerService fileInnerService;
    private final FileService fileService;
    private final String fileBasePath;

    public ExcelInnerWriterServiceImpl(ExcelInnerServiceImpl excelInnerService,
                                       FileInnerService fileInnerService,
                                       FileService fileService,
                                       UkerServerProperties ukerServerProperties) {
        this.excelInnerService = excelInnerService;
        this.fileInnerService = fileInnerService;
        this.fileService = fileService;
        this.fileBasePath = ukerServerProperties.getFileBasePath();
    }

    /**
     * 写入 excel
     */
    <R> String writeToExcel(@NonNull List<R> data, @NonNull Filepath toDir, @NonNull Class<R> clazz) throws Exception {
        excelInnerService.checkClassMapping(clazz);
        List<ExcelColumnDescriptor> columnDescriptors = excelInnerService.getColumnDescriptors(clazz, null);
        int sheetColumnLength = columnDescriptors.size();

        ExcelConfig excelConfig = clazz.getAnnotation(ExcelConfig.class);

        Workbook workbook = createWorkbook(excelConfig.filename());
        Sheet sheet = workbook.createSheet("sheet1");

        int r = writeSheetTitle(sheet, excelConfig.title(), sheetColumnLength);
        r = writeSheetHeader(sheet, columnDescriptors, sheetColumnLength, r);

        for (R obj : data) {
            Row row = sheet.createRow(r++);
            for (int j = 0; j < sheetColumnLength; j++) {
                Cell cell = row.createCell(j);
                ExcelColumnDescriptor columnDescriptor = columnDescriptors.get(j);
                ValueType valueType = columnDescriptor.calValueType();
                switch (valueType.getCellType()) {
                    case STRING:
                        cell.setCellValue((String) columnDescriptor.invokeGetter(obj));
                        break;
                    case NUMERIC:
                        String s = String.valueOf(columnDescriptor.invokeGetter(obj));
                        if ("".equals(s)) {
                            cell.setCellValue("");
                        } else {
                            cell.setCellValue(Double.parseDouble(s));
                        }
                        break;
                }
            }
        }

        String to = fileInnerService.getSecondaryFilePath(toDir) +
                (!"".equals(excelConfig.filename()) ? excelConfig.filename()
                        : fileInnerService.newRandomFilename(FileExtension.XLSX.getExtension()));
        FileOutputStream fileOutputStream = new FileOutputStream(new File(fileBasePath + to));
        workbook.write(fileOutputStream);
        workbook.close();
        fileOutputStream.close();

        return fileInnerService.filePathOut(to);
    }

    /**
     * 读取验证错误时导出错误行及错误说明
     */
    <R> String writeToExcel(List<Map<String, String>> errors, List<ExcelColumnDescriptor> columnDescriptors, Class<R> clazz) throws Exception {
        ExcelConfig excelConfig = clazz.getAnnotation(ExcelConfig.class);
        int sheetColumnLength = columnDescriptors.size();

        Workbook workbook = createWorkbook(excelConfig.filename());
        Sheet sheet = workbook.createSheet("sheet1");
        int r = writeSheetTitle(sheet, excelConfig.title(), sheetColumnLength);
        r = writeSheetHeader(sheet, columnDescriptors, sheetColumnLength, r);

        Font font = workbook.createFont();
        font.setColor(IndexedColors.RED.getIndex());
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);

        for (Map<String, String> error : errors) {
            Row row = sheet.createRow(r++);
            List<String> cols = Arrays.asList(error.get("cols").split(","));
            int j = 0;
            while (j < columnDescriptors.size()) {
                Cell cell = row.createCell(j);
                cell.setCellValue(error.get(String.valueOf(j)));
                if (cols.contains(String.valueOf(j))) {
                    cell.setCellStyle(cellStyle);
                }
                j++;
            }
            Cell cell = row.createCell(j++);
            String failMsg = error.get("failMsg");
            if (failMsg != null) {
                cell.setCellValue(failMsg);
                cell.setCellStyle(cellStyle);
            }
            row.createCell(j++).setCellValue(error.get("row"));
            row.createCell(j++).setCellValue(error.get("sheet"));
            row.createCell(j).setCellValue(error.get("excel"));
        }

        String to = fileInnerService.getSecondaryFilePath(Filepath.TEMP) +
                (!"".equals(excelConfig.filename()) ? excelConfig.filename()
                        : fileInnerService.newRandomFilename(FileExtension.XLSX.getExtension()));
        FileOutputStream fileOutputStream = new FileOutputStream(new File(fileBasePath + to));
        workbook.write(fileOutputStream);
        workbook.close();
        fileOutputStream.close();

        return to;
    }

    /**
     * 导出动态定义的表格行
     */
    String writeToExcel(@NonNull List<ExcelDynamicColumn> header, @NonNull List<List<Object>> data, @NonNull Filepath toDir, @NonNull String title) throws Exception {
        List<ExcelColumnDescriptor> columnDescriptors = excelInnerService.getDynamicColumnDescriptors(header, null);
        int sheetColumnLength = columnDescriptors.size();

        Workbook workbook = createWorkbook("");
        Sheet sheet = workbook.createSheet("sheet1");
        int r = writeSheetTitle(sheet, title, sheetColumnLength);
        r = writeSheetHeader(sheet, columnDescriptors, sheetColumnLength, r);

        for (List<Object> objs : data) {
            Row row = sheet.createRow(r++);
            for (int j = 0; j < sheetColumnLength; j++) {
                Cell cell = row.createCell(j);
                ExcelColumnDescriptor columnDescriptor = columnDescriptors.get(j).getChildDeepest();
                ValueType valueType = columnDescriptor.getValueType();
                switch (valueType.getCellType()) {
                    case STRING:
                        cell.setCellValue(String.valueOf(objs.get(j)));
                        break;
                    case NUMERIC:
                        String s = String.valueOf(objs.get(j));
                        if (s.equals("")) cell.setCellValue("");
                        else cell.setCellValue(Double.parseDouble(s));
                        break;
                }
            }
        }

        String to = fileInnerService.getSecondaryFilePath(toDir) + fileInnerService.newRandomFilename(FileExtension.XLSX.getExtension());
        FileOutputStream fileOutputStream = new FileOutputStream(new File(fileBasePath + to));
        workbook.write(fileOutputStream);
        workbook.close();
        fileOutputStream.close();

        return to;
    }

    private Workbook createWorkbook(String filename) {
        return ("".equalsIgnoreCase(filename) ?
                FileExtension.XLSX : fileService.getFileExtension(filename)) == FileExtension.XLSX ?
                new XSSFWorkbook() : new HSSFWorkbook();
    }

    private int writeSheetTitle(Sheet sheet, String title, int sheetColumnLength) {
        int r = 0;
        if (!"".equals(title)) {
            Row row = sheet.createRow(r);
            row.setHeightInPoints(30);
            Cell cell = row.createCell(0);
            cell.setCellValue(title);
            CellUtil.setAlignment(cell, HorizontalAlignment.CENTER);
            CellUtil.setVerticalAlignment(cell, VerticalAlignment.CENTER);
            sheet.addMergedRegion(new CellRangeAddress(r, r, 0, sheetColumnLength - 1));
            r++;
        }
        return r;
    }

    private int writeSheetHeader(Sheet sheet, List<ExcelColumnDescriptor> columnDescriptors, int sheetColumnLength, int r) {
        int sheetHeaderRowLength = excelInnerService.getSheetHeaderRowCount(columnDescriptors);
        int sheetHeaderLastRowNumber = r + sheetHeaderRowLength - 1;
        Map<Integer, Integer> sheetHeaderRangeStartRowNumber = new HashMap<>();
        for (int i = 0; i < sheetHeaderRowLength; i++) {
            Row row = sheet.createRow(r);
            boolean needRange = false;
            String lastTitle = null;
            String thisTitle;
            int startColumn = -1;
            for (int j = 0; j < sheetColumnLength; j++) {
                Cell cell = row.createCell(j);
                CellUtil.setAlignment(cell, HorizontalAlignment.CENTER);
                CellUtil.setVerticalAlignment(cell, VerticalAlignment.CENTER);
                ExcelColumnDescriptor columnDescriptor = columnDescriptors.get(j).getChildAt(i);
                if (columnDescriptor != null) {
                    String title = columnDescriptor.getExcelColConfig().title();
                    cell.setCellValue(title);
                    thisTitle = title;
                } else {
                    thisTitle = null;
                    sheetHeaderRangeStartRowNumber.putIfAbsent(j, r - 1);
                }
                if (needRange) {
                    if (!lastTitle.equals(thisTitle)) {
                        needRange = false;
                        lastTitle = thisTitle;
                        sheet.addMergedRegion(new CellRangeAddress(r, r, startColumn, j - 1));
                    }
                } else {
                    if (lastTitle != null && lastTitle.equals(thisTitle)) {
                        needRange = true;
                        startColumn = j - 1;
                    } else {
                        lastTitle = thisTitle;
                    }
                }
            }
            if (needRange) {
                sheet.addMergedRegion(new CellRangeAddress(r, r, startColumn, sheetColumnLength - 1));
            }
            r++;
        }
        for (int j = 0; j < sheetColumnLength; j++) {
            if (sheetHeaderRangeStartRowNumber.containsKey(j)) {
                sheet.addMergedRegion(new CellRangeAddress(sheetHeaderRangeStartRowNumber.get(j), sheetHeaderLastRowNumber, j, j));
            }
        }
        return r;
    }

}
